import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { Row, Col, Select, Table, Checkbox } from 'antd';
const Option = Select.Option;
import uuidV1 from 'uuid/v1';
import * as d3 from 'd3';
import {
	delNode, 
	addNode,
	makeConnect, 
	delConnect, 
	delConnects,
	markSelected
} from '../../action';

let Position = [0, 0];

const styles = {
	wrapper: {
    height: '100%',
    width: '100%',
    margin: '0px auto',
    position: 'relative'
	},
	wrapperView: {
		height: '100%',
    width: 'calc(100% - 50px)',
    margin: '0px auto',
    position: 'relative'
	},
	nodeComNode: {
		position: 'relative',
		left: 21,
		top: 50,
		cursor: 'move',
		fontSize: 30,
		color: 'rgb(89, 171, 227)'
	},
	nodeComRelation: {
		textAlign: 'center',
		margin: '0px auto',
		position: 'relative',
		top: 115,
		cursor: 'pointer'
	},
	relationEditor: {
		position: 'absolute',
		width: 'calc(100% - 100px)',
		margin: 0
	},
	title: {
		position: 'relative',
		textAlign: 'center',
		height: 40,
		lineHeight: '40px',
		fontSize: 18,
		fontWeight: 'bolder',
		color: '#777',
		textAlign: 'center',
		background: '#E8EBED'
	},
	contextMenu: {
    position: 'absolute',
    display: 'inline-block',
    borderStyle: 'solid',
    borderWidth: 1,
    borderColor: 'rgba(214, 214, 214, 0.8)' ,
    boxSizing: 'border-box',
    padding: '0px 20px',
    background: '#fff',
  },
  contextItem: {
	  cursor: 'pointer',
	  textAlign: 'left',
	  fontSize: '12px',
	  padding:'5px 0'
	}
}

const NODE_SIZE = 40;

class HandleField extends Component {
	constructor(props) {
		super(props);
		this.state = {
			chooseType: '',
			contextmenu: null
		}
	}
	shouldComponentUpdate(nextProps, nextState) {
		const { dealRelations, dealNodes, selectedComId, id } = this.props;
		console.log(dealRelations, dealNodes, selectedComId);
		const { chooseType } = this.state;
		if (dealNodes !== nextProps.dealNodes && nextProps.dealNodes) {
			this.update_node(nextProps.dealNodes);
		}
	
		if (dealRelations !== nextProps.dealRelations && nextProps.dealRelations && nextProps.dealNodes.length) {
			this.update_relations(nextProps.dealRelations, nextProps.dealNodes);
		}

		if (id !== nextProps.id) {
			console.log('id come')
		}

		if (this.state !== nextState) {
			return true
		}

		return false;
	}
	componentDidMount() {
		if (this.props.id) {
			this.props.dispatch(this.props.getRelationById(this.props.id))
		}
		const self = this;
		this.svg = d3.select(this.refs.relationEditor)
			.attr('cursor', (d) => {
				return self.state.chooseType === 'relation' ? 'crosshair' : 'auto'
			})
			.on('contextmenu', function() {
				d3.event.preventDefault();
				d3.event.stopPropagation();
				self.chooseComType('');
				self.props.updateFormType('', '');
				self.setState({
					contextmenu: null
				});
				self.props.dispatch(markSelected('clear'));
				d3.selectAll('g.node, circle').style('fill', 'rgb(89, 171, 227)')
					.style('stroke', 'rgb(89, 171, 227)')

				d3.selectAll('path.back').style('stroke', 'rgba(0, 0, 0, 0)')
				d3.selectAll('path.front').style('stroke', '#999999')
			})
			.on('click', function() {
				self.setState({
					contextmenu: null
				});
				// // d3.event.preventDefault();
				// // d3.event.stopPropagation();
				// self.chooseComType('');
				// self.props.updateFormType('', '');
				// self.setState({
				// 	contextmenu: null
				// });
				// self.props.dispatch(markSelected('clear'));
				// d3.selectAll('g.node, circle').style('fill', 'rgb(89, 171, 227)')
				// 	.style('stroke', 'rgb(89, 171, 227)')

				// d3.selectAll('path.back').style('stroke', 'rgba(0, 0, 0, 0)')
			})
		this.connectingLine = this.svg.append('line')
		  .classed('node-connect-line', true)
		  .attr('x1', 0)
		  .attr('y1', 0)
		  .attr('x2', 0)
		  .attr('y2', 0);
    let tmpDragNodes, tmpDragRelations;
		this.drag = d3.drag()
			.on('start', function(d, i) {
				if (self.state.chooseType === 'relation') {
					self.connectingLine
						.attr('x1', d.x)
						.attr('y1', d.y)
						.attr('x2', d.x)
						.attr('y2', d.y)
					self.startPoint = {...d};
				}
			})
			.on('drag', function(d, i) {
				if (self.state.chooseType === 'relation') {
					let m = d3.mouse(this);
					self.connectingLine
						.attr('x2', m[0]+d.x)
						.attr('y2', m[1]+d.y)
				} else {
					d.x += d3.event.dx;
	        d.y += d3.event.dy;
	        d3.select(this).attr('transform', d => `translate(${d.x}, ${d.y})`)
	        self.props.dealRelations.reduce((pre, item) => {
	        	const associateNode = item.associateNode;
	        	if (associateNode[1].id == d.id) {
	        		associateNode[1].x = d.x;
	        		associateNode[1].y = d.y;
	        	} else if (associateNode[0].id == d.id) {
	        		associateNode[0].x = d.x;
	        		associateNode[0].y = d.y;
	        	}
	        	return [...pre, item]
	        }, [])
	        self.update_relations(self.props.dealRelations);
				}
        // tmpDragRelations.attr('transform', d => {
        //   d.dx += d3.event.dy;
        //   d.dy += d3.event.dx;
        //   return `translate(${d.dy}, ${d.dx})`
        // });
      })
      .on('end', function(d) {
      	if (self.state.chooseType === 'relation') {
					self.connectingLine
						.attr('x1', 0)
						.attr('y1', 0)
						.attr('x2', 0)
						.attr('y2', 0)
					self.connect_end();
      	}
      })

  //   this.zoom = d3.zoom()
		// 	.scaleExtent([0.1, 4])
		// 	.translateExtent([[-2000, -1000], [this.width+2000, this.height+900]])
		// 	.on('zoom', () => {
		// 		this.container.attr("transform", d3.event.transform);
		// 	})
		// this.svg.call(this.zoom);
  //     .selectAll('g')
  //     .data([{x: 0, y: 0}])
  //     .enter()
  //     .append('g')
  //     .attr('transform', d => `translate(${d.x}, ${d.y})`);
		this.update_node(this.props.dealNodes);
		this.update_relations(this.props.dealRelations);
	}
	update_relations(relations, dealnodes) {
		// console.log('update_relations: ', relations);
		// console.log('dealnodes: ', dealnodes);
		let self = this;
		let nodeData = this.props.dealNodes;
		if (nodeData.length == 0) {
			nodeData = dealnodes;
		}
		let data = relations.map(i => {
			const associateNode = i.associateNode;
			let a = _.find(nodeData, j => j.id == associateNode[0].id);
			let b = _.find(nodeData, j => j.id == associateNode[1].id);
			return {
				id: i.id,
				direction: i.direction,
				selected: i.selected,
				alias: i.alias,
				start: associateNode[0].id,
				end: associateNode[1].id,
				x0: a.x,
				y0: a.y,
				x1: b.x,
				y1: b.y,
				type: i.type
			}
		});
		let connection_update = this.svg.selectAll('g.connection')
			.data(data, d => d.id)

		connection_update.select('path.front')
			.attr('d', d => this._make_line(d))
			.attr('marker-start', (d) => {
				if (d.direction === 1 || d.direction === 2) {
					return 'url(#start)'
				} else {
					return ''
				}
			})
			.attr('marker-end', (d) => {
				if (d.direction === 0 || d.direction === 2) {
					return 'url(#end)'
				} else {
					return ''
				}
			})

		connection_update.select('path.back')
			.attr('d', d => this._make_line(d))

		// connection.append('text')

		// connection_update.append('text')
		// 	.classed('connectionText', true)
  //     .style('text-anchor', 'middle')
  //     .style('fill', '#555')
  //     .attr('stroke', '#555')

    connection_update.select('text.connectionText')
    	.attr('x', (d) => {
      	return (d.x0 + d.x1)/2
      })
      .attr('y', (d) => {
      	return (d.y0 + d.y1)/2
      })
	    .text(d => {
    		if (d.type) {
    			return d.type;
    		} else {
    			return '';
    		}
      })

		connection_update.exit().remove();

		let connection = connection_update.enter()
			.merge(connection_update)
			.append('g')
			.classed('connection', true)

		connection.append('path')
			.classed('front', true)
			.attr('d', d => this._make_line(d))
			.attr('marker-start', (d) => {
				if (d.direction === 1 || d.direction === 2) {
					return 'url(#start)'
				} else {
					return ''
				}
			})
			.attr('marker-end', (d) => {
				if (d.direction === 0 || d.direction === 2) {
					return 'url(#end)'
				} else {
					return ''
				}
			})
			.style('stroke', function(d) {
				if (d.selected) {
					return 'rgba(0, 149, 255, 0.3)';
				} else {
					return '#999999';
				}
			})

		connection.append('path')
			.classed('back', true)
			.attr('d', d => this._make_line(d))
			.style('stroke', function(d) {
				if (d.selected) {
					return 'rgba(0, 149, 255, 0.3)';
				} else {
					return 'rgba(0, 0, 0, 0)';
				}
			})
			.on('mouseover', function() {
				d3.select(this)
					.style('stroke', function(d) {
						return 'rgba(0, 149, 255, 0.3)'
					})
			})
			.on('mouseout', function() {
				d3.select(this)
					.style('stroke', function(d) {
						if (d.selected) {
							return 'rgba(0, 149, 255, 0.3)';
						} else {
							return 'rgba(0, 0, 0, 0)';
						}
					})
			})
			.on('click', function(d) {
				d3.select(this).style('stroke', function(d) {
					self.props.updateFormType('relation', d.id);
					// update reduxx dealnodes
					self.props.dispatch(markSelected('clear'));
					self.props.dispatch(markSelected('relation', d.id));
					return 'rgba(0, 149, 255, 0.3)';
				})
			})
			.on('contextmenu', function(d) {
				d3.event.preventDefault();
				d3.event.stopPropagation();
			})

		connection.append('text')
			.classed('connectionText', true)
      .style('text-anchor', 'middle')
      .style('fill', '#555')
      .attr('stroke', '#555')
      .attr('x', (d) => {
      	return (d.x0 + d.x1)/2
      })
      .attr('y', (d) => {
      	return (d.y0 + d.y1)/2
      })
	    .text(d => {
    		if (d.type) {
    			return d.type;
    		} else {
    			return '';
    		}
      })

    // this.connection = connection.merge(connection_update);
	}
	_make_line(d) {
		let line = d3.line()
			.x(d => d[0])
			.y(d => d[1])
			// .curve(d3.curveBundle);

		// let data = [
		// 	[d.x0, d.y0], 
		// 	[d.x0*3/4+d.x1/4, d.y0 ],
		// 	[d.x0/2+d.x1/2, d.y0/2+d.y1/2 ],
		// 	[d.x0/4+d.x1*3/4, d.y1 ],
		// 	[d.x1, d.y1]
		// ];
		let dx = Math.abs(d.x1 - d.x0);
		let dy = Math.abs(d.y1 - d.y0);
		let hypotenuse = Math.sqrt(dx * dx + dy * dy);
		let final_dx0 = d.x1 - d.x0 > 0 ? (d.x0 + (30 * dx)/hypotenuse) : (d.x0 - (30 * dx)/hypotenuse);
		let final_dy0 = d.y1 - d.y0 < 0 ? (d.y0 - (30 * dy)/hypotenuse) : (d.y0 + (30 * dy)/hypotenuse);
		let final_dx1 = d.x1 - d.x0 > 0 ? (d.x1 - (30 * dx)/hypotenuse) : (d.x1 + (30 * dx)/hypotenuse);
		let final_dy1 = d.y1 - d.y0 < 0 ? (d.y1 + (30 * dy)/hypotenuse) : (d.y1 - (30 * dy)/hypotenuse);
		let data = [[final_dx0, final_dy0], [final_dx1, final_dy1]];
		return line(data)
	}
	connect_end() {
  	const self = this;
  	const { startPoint, endPoint } = this;
  	let flag = false;
  	this.props.dealRelations.map(item => {
  		if ((item.associateNode[0].id === startPoint.id || item.associateNode[0].id === endPoint.id) && (item.associateNode[1].id === startPoint.id || item.associateNode[1].id === endPoint.id)) {
  			flag = true;
  		}
  	})
  	if (startPoint && endPoint && startPoint.id !== endPoint.id && !flag) {
  		console.log('a new connection has been created');
  		this.props.dispatch(makeConnect([startPoint, endPoint], uuidV1()))
  		// this.dealRelations = this.dealRelations.concat([]);
  		// this.update_relations(this.dealRelations);
  	}
  	this.startPoint = null;
  	this.endPoint = null;
  }
	_dragStart(event) {
    event.dataTransfer.setData('Text', '');
    this.chooseComType('node');
    document.addEventListener('dragover', this._dragOver, false);
  }

  _dragOver(event) {
    // event.preventDefault();
    Position = [event.clientX, event.clientY]
  }

  _dragEnd(data, event) {
    event.preventDefault();
    document.removeEventListener('dragover', this._dragOver, false);
    let s = ReactDOM.findDOMNode(this.refs.relationEditor).getBoundingClientRect();
    this.svgHeight = s.height;
    this.svgWidth = s.width;
    let offsetX = Position[0] - s.left;
    let offsetY = Position[1] - s.top;
    let x = offsetX/s.width;
    let y = offsetY/s.height;
    if (x>=1 || x<=0 || y>=1 || y<=0) {
      return
    }
    this._addComp({
    	id: uuidV1(),
      x: x * s.width,
      y: y * s.height,
      ...data,
    });
  }

  _addComp(comp) {
  	this.props.dispatch(addNode(comp));
    // this.dealNodes = [...this.dealNodes, comp];
    // this.dealRelations = [{'id': id, 'parent': ''}];

    // this.update_node();
  }

  update_node(dealNodes) {
  	let self = this;
  	let nodesUpdate = this.svg.selectAll('g.node')
  		.data(dealNodes, d => d.id);
  	let nodesExit = nodesUpdate.exit().remove();
  	let nodesEnter = nodesUpdate.enter().append('g');
  	let nodes = nodesEnter
  		.merge(nodesUpdate)
  		.attr('class', 'node')
  		.attr('cursor', 'move')
  		.attr('transform', d => `translate(${d.x}, ${d.y})`)
  		.style('fill', d => {
      	if (d.selected) {
      		return '#597FCA'
      	} else {
      		return 'rgb(89, 171, 227)'
      	}
      })
  		.call(this.drag)
			.on('click', function(d) {
				d3.select(this).style('fill', function(d) {
					self.props.updateFormType('node', d.id);
					// update reduxx dealRelations
					self.props.dispatch(markSelected('clear'));
					self.props.dispatch(markSelected('node', d.id));
					return '#597FCA';
				})
			})
			.on('contextmenu', function(d) {
				d3.event.preventDefault();
				d3.event.stopPropagation();
				self.setState({
					contextmenu: {
						items: [
							{name: '删除', action: self.deleteCurrent.bind(null, d.id, 'node')}
						],
						ctxmenuPosition: [d.x + 100 + d3.mouse(this)[0], d.y + d3.mouse(this)[1]]
					}
				})
			})
  	nodesEnter.append('circle')
  		.merge(nodesUpdate)
      .attr('r', 30)
      .on('mouseover', function() {
        if (self.state.chooseType === 'relation') {
          d3.select(this).style('fill', '#597FCA')
          	.attr('cursor', 'crosshair')
        } else {
        	d3.select(this).style('fill', '#597FCA')
          	.attr('cursor', 'move')
        }
      })
      .style('fill', d => {
      	if (d.selected) {
      		return '#597FCA'
      	} else {
      		return 'rgb(89, 171, 227)'
      	}
      })
      .style('stroke', d => {
        if (d.selected) {
      		return '#597FCA'
      	} else {
      		return 'rgb(89, 171, 227)'
      	}
      })
      .on('mouseout', function(d) {
      	if (d.selected) {
      		d3.select(this).style('fill', '#597FCA')
      	} else {
      		d3.select(this).style('fill', 'rgb(89, 171, 227)')
      	}
      });
    nodesEnter.append('text')
    	.classed('nodeText', true)
      .style('text-anchor', 'middle')
      .style('font-size', '16px')
      .style('fill', '#555')
      .attr('y', 45)
      .attr('stroke', '#555')

    this.svg.selectAll('text.nodeText')
	    .text(d => {
    		if (d.alias) {
    			return d.alias;
    		} else {
    			return '';
    		}
      })

    this.svg.selectAll('circle')
	    .style('fill', d => {
      	if (d.selected) {
      		return '#597FCA'
      	} else {
      		return 'rgb(89, 171, 227)'
      	}
      })

    this.svg.selectAll('g.node')
    	.on('mouseover', function(d) {
  			self.endPoint = {...d};
  		})
  		.on('mouseout', function(d) {
				self.endPoint = null;
			})
  }
  deleteCurrent = (nodeId, componentType) => {
  	this.props.dispatch(delNode(nodeId, componentType));
  }
	renderAction = (text, record) => {
		return (
			<div>
				<span style={{color: '#2baee9', cursor: 'pointer'}}>编辑</span>
				<span style={{color: '#2baee9', cursor: 'pointer'}}> | 查看</span>
			</div>
		)
	}
	chooseComType = (type) => {
		this.setState({chooseType: type}, () => {
			if (type === 'relation') {
				this.svg.attr('cursor', 'crosshair')
			} else {
				this.svg.attr('cursor', 'auto')
			}
		})
	}
	render() {
		const { contextmenu } = this.state;
		const columns = this.columns;
		const dataSource = [];
		return (
			<div style={this.props.handleType === 'view' ? {...styles.wrapperView, cursor: this.state.chooseType == 'relation' ? 'crosshair' : 'auto'} : {...styles.wrapper, cursor: this.state.chooseType == 'relation' ? 'crosshair' : 'auto'}}>
				<div style={{width: 100, height: '100%', background: '#F3F5F6', float: 'left'}}>
					<div>
						<i className={'icon iconfont icon-dian'}
							 style={{...styles.nodeComNode}}
							 draggable="true"
							 onDragStart={this._dragStart.bind(this)}
							 onDragEnd={this._dragEnd.bind(this, {
                type: 'node'
              })}></i>
						<span style={{position: 'relative', top: 85, color: '#999999', left: -17}}>节点</span>
          </div>
          <div style={styles.nodeComRelation} onClick={this.chooseComType.bind(null, 'relation')}>
          	<i className={'icon iconfont icon-bian1'}
          		 style={{fontSize: 26, position: 'relative', left: 4, color: 'rgb(89, 171, 227)', cursor: this.state.chooseType == 'relation' ? 'crosshair' : 'pointer'}}
							 onClick={this.chooseComType.bind(null, 'relation')}></i>
            <span style={{position: 'relative', top: 25, left: -23, color: '#999999'}}>边</span>
          </div>
				</div>
				<div>
					<svg ref="relationEditor" style={styles.relationEditor}>
       		  <defs>
					    <marker
					      id="start"
					      markerUnits="strokeWidth"
					      markerWidth="13"
					      markerHeight="13"
					      viewBox="-8 0 12 12"
					      refX="-8"
					      refY="6"
					      orient="auto">
					      <path d="M 0 0 L -8 6 L 0 12 z" style={{fill: "#999999"}}></path>
					    </marker>
					    <marker
					      id="end"
					      markerUnits="strokeWidth"
					      markerWidth="13"
					      markerHeight="13"
					      viewBox="0 0 12 12"
					      refX="8"
					      refY="6"
					      orient="auto">
					      <path d="M 0 0 L 8 6 L 0 12 z" style={{fill: "#999999"}}></path>
					    </marker>
					  </defs>
		      </svg>
				</div>
				{
					contextmenu ?
					<div style={{...styles.contextMenu, left: contextmenu.ctxmenuPosition[0], top: contextmenu.ctxmenuPosition[1]}}>
	          {
	            contextmenu.items.map((item, index) => {
	              return <div key={index}
	                className="disSelect"
	                onClick={() => {
	                  item.action();
	                  this.setState({
	                    contextmenu: null,
	                  })
	                }}
	                onContextMenu={(ev) => {ev.preventDefault()}}
	                style={styles.contextItem}>{item.name}</div>
	            })
	          }
	        </div>
	        :
	        null
				}
			</div>
		)
	}
}

function mapState2Props(state) {
	let { graphData: {relationships, nodes, dealNodes, dealRelations} } = state;
	return { relationships, nodes, dealNodes, dealRelations };
}

HandleField = connect(mapState2Props)(HandleField);

export default HandleField;
